package com.codemonkey.lq.common.util;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

public final class ReflectHelper {
	private ReflectHelper() {
		throw new Error("Can not instantize ! ! !");
	}
	
	   /**
     * 以控制台的方式输出一个类中完整定义,包括: 1. 完整类名 2. 继承的类 3. 实现的接口 4. 所有的字段 5. 所有的方法
     * 
     * @param clz
     */
    public static void printClassDefinition(Class clz) {

        String clzModifier = getModifier(clz.getModifiers());
        if (clzModifier != null && !clzModifier.equals("")) {
            clzModifier = clzModifier + " ";
        }
        String superClz = clz.getSuperclass().getName();
        if (superClz != null && !superClz.equals("")) {
            superClz = "extends " + superClz;
        }

        Class[] interfaces = clz.getInterfaces();

        String inters = "";
        for (int i = 0; i < interfaces.length; i++) {
            if (i == 0) {
                inters += "implements ";
            }
            inters += interfaces[i].getName();
        }

        System.out.println(clzModifier + clz.getName() + " " + superClz + " " + inters);
        System.out.println("{");

        Field[] fields = clz.getDeclaredFields();
        for (int i = 0; i < fields.length; i++) {
            String modifier = getModifier(fields[i].getModifiers());
            if (modifier != null && !modifier.equals("")) {
                modifier = modifier + " ";
            }
            String fieldName = fields[i].getName();
            String fieldType = fields[i].getType().getName();
            System.out.println("    " + modifier + fieldType + " " + fieldName + ";");
        }

        System.out.println();

        Method[] methods = clz.getDeclaredMethods();
        for (int i = 0; i < methods.length; i++) {
            Method method = methods[i];

            String modifier = getModifier(method.getModifiers());
            if (modifier != null && !modifier.equals("")) {
                modifier = modifier + " ";
            }

            String methodName = method.getName();

            Class returnClz = method.getReturnType();
            String retrunType = returnClz.getName();

            Class[] clzs = method.getParameterTypes();
            String paraList = "(";
            for (int j = 0; j < clzs.length; j++) {
                paraList += clzs[j].getName();
                if (j != clzs.length - 1) {
                    paraList += ", ";
                }
            }
            paraList += ")";

            clzs = method.getExceptionTypes();
            String exceptions = "";
            for (int j = 0; j < clzs.length; j++) {
                if (j == 0) {
                    exceptions += "throws ";
                }

                exceptions += clzs[j].getName();

                if (j != clzs.length - 1) {
                    exceptions += ", ";
                }
            }

            exceptions += ";";

            String methodPrototype = modifier
                                     + retrunType
                                     + " "
                                     + methodName
                                     + paraList
                                     + exceptions;

            System.out.println("    " + methodPrototype);

        }
        System.out.println("}");
    }

    static String getModifier(int modifier) {
        String result = "";
        switch (modifier) {
        case Modifier.PRIVATE:
            result = "private";
            break;
        case Modifier.PUBLIC:
            result = "public";
            break;
        case Modifier.PROTECTED:
            result = "protected";
            break;
        case Modifier.ABSTRACT:
            result = "abstract";
            break;
        case Modifier.FINAL:
            result = "final";
            break;
        case Modifier.NATIVE:
            result = "native";
            break;
        case Modifier.STATIC:
            result = "static";
            break;
        case Modifier.SYNCHRONIZED:
            result = "synchronized";
            break;
        case Modifier.STRICT:
            result = "strict";
            break;
        case Modifier.TRANSIENT:
            result = "transient";
            break;
        case Modifier.VOLATILE:
            result = "volatile";
            break;
        case Modifier.INTERFACE:
            result = "interface";
            break;
        }
        return result;
    }
	

	// 获取一个泛型类的实际泛型类型
	// 实际应用见《改善Java程序的151个建议》之建议109
	@SuppressWarnings("unchecked")
	public static <T> Class<T> getGenricClassType(Class clz) {
		Type type = clz.getGenericSuperclass();

		if (type instanceof ParameterizedType) {
			ParameterizedType pt = (ParameterizedType) type;
			Type[] types = pt.getActualTypeArguments();
			if (types.length > 0 && types[0] instanceof Class) {
				// 若有多个泛型参数,依据位置索引返回
				return (Class) types[0];
			}
		}
		return (Class) Object.class;

	}

	/** 
	 * http://blog.csdn.net/sunyujia/article/details/2501709
	 * 
	 * 利用递归找一个类的指定方法，如果找不到，去父亲里面找直到最上层Object对象为止。 
	 *  
	 * @param clazz 
	 *            目标类 
	 * @param methodName 
	 *            方法名 
	 * @param classes 
	 *            方法参数类型数组 
	 * @return 方法对象 
	 * @throws Exception 
	 */
	public static Method getMethod(Class clazz, String methodName, final Class[] classes)
			throws Exception {
		Method method = null;
		try {
			method = clazz.getDeclaredMethod(methodName, classes);
		} catch (NoSuchMethodException e) {
			try {
				method = clazz.getMethod(methodName, classes);
			} catch (NoSuchMethodException ex) {
				if (clazz.getSuperclass() == null) {
					return method;
				} else {
					method = getMethod(clazz.getSuperclass(), methodName, classes);
				}
			}
		}
		return method;
	}

	/** 
	 *  利用java反射调用类的的私有方法 	http://blog.csdn.net/sunyujia/article/details/2501709
	 *  
	 * @param obj 
	 *            调整方法的对象 
	 * @param methodName 
	 *            方法名 
	 * @param classes 
	 *            参数类型数组 
	 * @param objects 
	 *            参数数组 
	 * @return 方法的返回值 
	 */
	public static Object invoke(final Object obj, final String methodName, final Class[] classes,
			final Object[] objects) {
		try {
			Method method = getMethod(obj.getClass(), methodName, classes);
			method.setAccessible(true);// 调用private方法的关键一句话  
			return method.invoke(obj, objects);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	public static Object invoke(final Object obj, final String methodName, final Class[] classes) {
		return invoke(obj, methodName, classes, new Object[] {});
	}

	public static Object invoke(final Object obj, final String methodName) {
		return invoke(obj, methodName, new Class[] {}, new Object[] {});
	}

	/**
	 * 调用指定类的实例的指定方法
	 */
	@SuppressWarnings("unchecked")
	public static <T> T invokeInstanceMethod(final String classFullName, final String methodName,
			final Object... params) {
		Class<?> clz = getSpecialClass_from_string(classFullName);
		if (clz == null) {
			String errorMsg = String.format("无法找到指定类 %s ,请确认相应的jar包存在！", classFullName);
			System.out.println(errorMsg);
			return null;
		}

		Object obj = newInstance_for_class(clz);
		if (obj == null) {
			String errorMsg = String.format("无法实例化 %s ！", classFullName);
			System.out.println(errorMsg);
			return null;
		}

		return (T) callMethod(obj, methodName, params);
	}

	private static Class<?> getSpecialClass_from_string(String className) {
		Class<?> cls = null;
		try {
			cls = Class.forName(className);
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}

		return cls;
	}

	private static Object newInstance_for_class(Class<?> cls) {
		Object obj = null;
		try {
			obj = cls.newInstance();
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		}

		return obj;
	}

	private static Object callMethod(Object obj, String methodName, final Object... params) {
		//		String promtyMsg = String.format("开始使用反射进行方法调用！！！\n类名:%s;;;方法名:%s", obj.getClass()
		//				.getSimpleName(), methodName);
		//		System.out.println(promtyMsg);		

		Class<?>[] paramClassArr = new Class<?>[params.length];
		for (int i = 0; i < params.length; i++) {
			paramClassArr[i] = params[i].getClass();
		}

		Method mt = null;
		try {
			mt = obj.getClass().getMethod(methodName, paramClassArr);
		} catch (NoSuchMethodException e) {
			e.printStackTrace();
		} catch (SecurityException e) {
			e.printStackTrace();
		}

		Object result = null;
		try {
			result = mt.invoke(obj, params);
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}

		//		System.out.println("使用反射进行方法调用结束！！！");

		return result;
	}

	// ------------------------------------
	/** 
	 *  
	 * @Description:调用只有一个参数的私有方法 
	 * @since 1.0.0 
	 * @Date:2013-1-23 下午2:54:00 
	 * @param methodHostInstance 
	 * @param methodName 
	 * @param arg 
	 * @return Object 
	 */
	public static Object invokePrivateMethod(Object methodHostInstance, String methodName,
			Object arg) {
		Class<?>[] parameterTypes = { arg.getClass() };
		Object[] args = { arg };
		return invokePrivateMethod(methodHostInstance, methodName, parameterTypes, args);
	}

	/** 
	 *  
	 * @Description:调用有多个参数的私有方法 
	 * @since 1.0.0 
	 * @Date:2013-1-23 下午2:54:40 
	 * @param methodHostInstance 
	 * @param methodName 
	 * @param parameterTypes 
	 * @param args 
	 * @return Object 
	 */
	public static Object invokePrivateMethod(Object methodHostInstance, String methodName,
			Class<?>[] parameterTypes, Object[] args) {
		try {
			Method method = methodHostInstance.getClass().getDeclaredMethod(methodName,
					parameterTypes);
			method.setAccessible(true);
			try {
				return method.invoke(methodHostInstance, args);
			} catch (IllegalArgumentException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			} catch (InvocationTargetException e) {
				e.printStackTrace();
			}
			method.setAccessible(false);
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (NoSuchMethodException e) {
			e.printStackTrace();
		}
		return null;
	}
}
